// [z-paging]核心js

import zStatic from './z-paging-static';
import c from './z-paging-constant';
import u from './z-paging-utils';

import zPagingRefresh from '../components/z-paging-refresh';
import zPagingLoadMore from '../components/z-paging-load-more';
import zPagingEmptyView from '../../z-paging-empty-view/z-paging-empty-view';

// modules
import dataHandleModule from './modules/data-handle';
import i18nModule from './modules/i18n';
import nvueModule from './modules/nvue';
import emptyModule from './modules/empty';
import refresherModule from './modules/refresher';
import loadMoreModule from './modules/load-more';
import loadingModule from './modules/loading';
import scrollerModule from './modules/scroller';
import backToTopModule from './modules/back-to-top';
import virtualListModule from './modules/virtual-list';

import Enum from './z-paging-enum';

const systemInfo = uni.getSystemInfoSync();

// #ifdef APP-NVUE
const weexDom = weex.requireModule('dom');
// #endif

export default {
  name: 'z-paging',
  components: {
    zPagingRefresh,
    zPagingLoadMore,
    zPagingEmptyView,
  },
  mixins: [dataHandleModule, i18nModule, nvueModule, emptyModule, refresherModule, loadMoreModule, loadingModule, scrollerModule, backToTopModule, virtualListModule],
  data() {
    return {
      //--------------静态资源---------------
      base64Arrow: zStatic.base64Arrow,
      base64Flower: zStatic.base64Flower,
      base64BackToTop: zStatic.base64BackToTop,

      //-------------全局数据相关--------------
      //当前加载类型
      loadingType: Enum.LoadingType.Refresher,
      requestTimeStamp: 0,
      chatRecordLoadingMoreText: '',
      wxsPropType: '',
      renderPropScrollTop: -1,
      checkScrolledToBottomTimeOut: null,
      systemInfo: null,
      cssSafeAreaInsetBottom: -1,
      cacheTopHeight: -1,

      //--------------状态&判断---------------
      insideOfPaging: -1,
      isLoadFailed: false,
      isIos: systemInfo.platform === 'ios',
      disabledBounce: false,
      fromCompleteEmit: false,
      disabledCompleteEmit: false,

      //---------------wxs相关---------------
      wxsIsScrollTopInTopRange: true,
      wxsScrollTop: 0,
      wxsPageScrollTop: 0,
      wxsOnPullingDown: false,
    };
  },
  props: {
    //调用complete后延迟处理的时间，单位为毫秒，默认0毫秒，优先级高于minDelay
    delay: {
      type: [Number, String],
      default: u.gc('delay', 0),
    },
    //触发@query后最小延迟处理的时间，单位为毫秒，默认0毫秒，优先级低于delay（假设设置为300毫秒，若分页请求时间小于300毫秒，则在调用complete后延迟[300毫秒-请求时长]；若请求时长大于300毫秒，则不延迟），当show-refresher-when-reload为true或reload(true)时，其最小值为400
    minDelay: {
      type: [Number, String],
      default: u.gc('minDelay', 0),
    },
    //设置z-paging的style，部分平台(如微信小程序)无法直接修改组件的style，可使用此属性代替
    pagingStyle: {
      type: Object,
      default: function () {
        return u.gc('pagingStyle', {});
      },
    },
    //z-paging的高度，优先级低于pagingStyle中设置的height；传字符串，如100px、100rpx、100%
    height: {
      type: String,
      default: u.gc('height', ''),
    },
    //z-paging的宽度，优先级低于pagingStyle中设置的width；传字符串，如100px、100rpx、100%
    width: {
      type: String,
      default: u.gc('width', ''),
    },
    //z-paging的背景色，优先级低于pagingStyle中设置的background。传字符串，如"#ffffff"
    bgColor: {
      type: String,
      default: u.gc('bgColor', ''),
    },
    //设置z-paging的容器(插槽的父view)的style
    pagingContentStyle: {
      type: Object,
      default: function () {
        return u.gc('pagingContentStyle', {});
      },
    },
    //z-paging是否自动高度，若自动高度则会自动铺满屏幕
    autoHeight: {
      type: Boolean,
      default: u.gc('autoHeight', false),
    },
    //z-paging是否自动高度时，附加的高度，注意添加单位px或rpx，若需要减少高度，则传负数
    autoHeightAddition: {
      type: [Number, String],
      default: u.gc('autoHeightAddition', '0px'),
    },
    //loading(下拉刷新、上拉加载更多)的主题样式，支持black，white，默认black
    defaultThemeStyle: {
      type: String,
      default: u.gc('defaultThemeStyle', 'black'),
    },
    //z-paging是否使用fixed布局，若使用fixed布局，则z-paging的父view无需固定高度，z-paging高度默认为100%，默认为是(当使用内置scroll-view滚动时有效)
    fixed: {
      type: Boolean,
      default: u.gc('fixed', true),
    },
    //是否开启底部安全区域适配
    safeAreaInsetBottom: {
      type: Boolean,
      default: u.gc('safeAreaInsetBottom', false),
    },
    //开启底部安全区域适配后，是否使用placeholder形式实现，默认为否。为否时滚动区域会自动避开底部安全区域，也就是所有滚动内容都不会挡住底部安全区域，若设置为是，则滚动时滚动内容会挡住底部安全区域，但是当滚动到底部时才会避开底部安全区域
    useSafeAreaPlaceholder: {
      type: Boolean,
      default: u.gc('useSafeAreaPlaceholder', false),
    },
    //slot="top"的view的z-index，默认为99，仅使用页面滚动时有效
    topZIndex: {
      type: Number,
      default: u.gc('topZIndex', 99),
    },
    //z-paging内容容器父view的z-index，默认为1
    superContentZIndex: {
      type: Number,
      default: u.gc('superContentZIndex', 1),
    },
    //z-paging内容容器部分的z-index，默认为10
    contentZIndex: {
      type: Number,
      default: u.gc('contentZIndex', 10),
    },
    //使用页面滚动时，是否在不满屏时自动填充满屏幕，默认为是
    autoFullHeight: {
      type: Boolean,
      default: u.gc('autoFullHeight', true),
    },
    //是否监听列表触摸方向改变，默认为否
    watchTouchDirectionChange: {
      type: Boolean,
      default: u.gc('watchTouchDirectionChange', false),
    },
  },
  created() {
    if (this.createdReload && !this.refresherOnly && this.auto) {
      this._startLoading();
      this._preReload();
    }
  },
  mounted() {
    this.wxsPropType = u.getTime().toString();
    this.renderJsIgnore;
    if (!this.createdReload && !this.refresherOnly && this.auto) {
      this.$nextTick(() => {
        this._preReload();
      });
    }
    this.finalUseCache && this._setListByLocalCache();
    let delay = 0;
    // #ifdef H5 || MP
    delay = 100;
    // #endif
    this.$nextTick(() => {
      this.systemInfo = uni.getSystemInfoSync();
      !this.usePageScroll && this.autoHeight && this._setAutoHeight();
      this.loaded = true;
    });
    this.updatePageScrollTopHeight();
    this.updatePageScrollBottomHeight();
    this._updateLeftAndRightWidth();
    if (this.finalRefresherEnabled && this.useCustomRefresher) {
      this.$nextTick(() => {
        this.isTouchmoving = true;
      });
    }
    this._onEmit();
    // #ifdef APP-NVUE
    if (!this.isIos && !this.useChatRecordMode) {
      this.nLoadingMoreFixedHeight = true;
    }
    this._nUpdateRefresherWidth();
    // #endif
    // #ifndef APP-NVUE
    this.finalUseVirtualList && this._virtualListInit();
    // #endif
    // #ifndef APP-PLUS
    this.$nextTick(() => {
      setTimeout(() => {
        this._getCssSafeAreaInsetBottom();
      }, delay);
    });
    // #endif
  },
  destroyed() {
    this._offEmit();
  },
  // #ifdef VUE3
  unmounted() {
    this._offEmit();
  },
  // #endif
  watch: {
    defaultThemeStyle: {
      handler(newVal) {
        if (newVal.length) {
          this.finalRefresherDefaultStyle = newVal;
        }
      },
      immediate: true,
    },
    autoHeight(newVal) {
      this.loaded && !this.usePageScroll && this._setAutoHeight(newVal);
    },
    autoHeightAddition(newVal) {
      this.loaded && !this.usePageScroll && this.autoHeight && this._setAutoHeight(newVal);
    },
  },
  computed: {
    finalPagingStyle() {
      const pagingStyle = this.pagingStyle;
      if (!this.systemInfo) return pagingStyle;
      const windowTop = this.windowTop;
      const windowBottom = this.windowBottom;
      if (!this.usePageScroll && this.fixed) {
        if (windowTop && !pagingStyle.top) {
          pagingStyle.top = windowTop + 'px';
        }
        if (windowBottom && !pagingStyle.bottom) {
          pagingStyle.bottom = windowBottom + 'px';
        }
      }
      if (this.bgColor.length && !pagingStyle['background']) {
        pagingStyle['background'] = this.bgColor;
      }
      if (this.height.length && !pagingStyle['height']) {
        pagingStyle['height'] = this.height;
      }
      if (this.width.length && !pagingStyle['width']) {
        pagingStyle['width'] = this.width;
      }
      return pagingStyle;
    },
    finalLowerThreshold() {
      return u.convertTextToPx(this.lowerThreshold);
    },
    finalPagingContentStyle() {
      if (this.contentZIndex != 1) {
        this.pagingContentStyle['z-index'] = this.contentZIndex;
        this.pagingContentStyle['position'] = 'relative';
      }
      return this.pagingContentStyle;
    },
    safeAreaBottom() {
      if (!this.systemInfo) return 0;
      let safeAreaBottom = 0;
      // #ifdef APP-PLUS
      safeAreaBottom = this.systemInfo.safeAreaInsets.bottom || 0;
      // #endif
      // #ifndef APP-PLUS
      safeAreaBottom = this.cssSafeAreaInsetBottom === -1 ? 0 : this.cssSafeAreaInsetBottom;
      // #endif
      return safeAreaBottom;
    },
    renderJsIgnore() {
      if ((this.usePageScroll && this.useChatRecordMode) || !this.refresherEnabled || !this.useCustomRefresher) {
        this.$nextTick(() => {
          this.renderPropScrollTop = 10;
        });
      }
      return 0;
    },
    windowHeight() {
      return !this.systemInfo ? 0 : this.systemInfo.windowHeight || 0;
    },
    windowTop() {
      //暂时修复vue3中隐藏系统导航栏后windowTop获取不正确的问题，具体bug详见https://ask.dcloud.net.cn/question/141634
      //感谢litangyu！！https://github.com/SmileZXLee/uni-z-paging/issues/25
      // #ifdef VUE3 && H5
      const pageHeadNode = document.getElementsByTagName('uni-page-head');
      if (!pageHeadNode.length) return 0;
      // #endif
      return !this.systemInfo ? 0 : this.systemInfo.windowTop || 0;
    },
    windowBottom() {
      if (!this.systemInfo) return 0;
      let windowBottom = this.systemInfo.windowBottom || 0;
      if (this.safeAreaInsetBottom && !this.useSafeAreaPlaceholder) {
        windowBottom += this.safeAreaBottom;
      }
      return windowBottom;
    },
    isOldWebView() {
      // #ifndef APP-NVUE || MP-KUAISHOU
      try {
        const systemInfos = systemInfo.system.split(' ');
        const deviceType = systemInfos[0];
        const version = parseInt(systemInfos[1].slice(0, 1));
        if ((deviceType === 'iOS' && version <= 10) || (deviceType === 'Android' && version <= 6)) {
          return true;
        }
      } catch (e) {
        return false;
      }
      // #endif
      return false;
    },
    isIosAndH5() {
      // #ifndef H5
      return false;
      // #endif
      return this.isIos;
    },
  },
  methods: {
    //当前版本号
    getVersion() {
      return `z-paging v${zConstant.version}`;
    },
    //设置nvue List的specialEffects
    setSpecialEffects(args) {
      this.setListSpecialEffects(args);
    },
    //与setSpecialEffects等效，兼容旧版本
    setListSpecialEffects(args) {
      this.nFixFreezing = args && Object.keys(args).length;
      if (this.isIos) {
        this.privateRefresherEnabled = 0;
      }
      if (!this.usePageScroll) {
        this.$refs['zp-n-list'].setSpecialEffects(args);
      }
    },
    //使手机发生较短时间的振动（15ms）
    _doVibrateShort() {
      // #ifdef APP-PLUS
      if (this.isIos) {
        const UISelectionFeedbackGenerator = plus.ios.importClass('UISelectionFeedbackGenerator');
        const feedbackGenerator = new UISelectionFeedbackGenerator();
        feedbackGenerator.init();
        setTimeout(() => {
          feedbackGenerator.selectionChanged();
        }, 0);
      } else {
        plus.device.vibrate(15);
      }
      // #endif
      // #ifndef APP-PLUS
      uni.vibrateShort();
      // #endif
    },
    //检测scrollView是否要铺满屏幕
    _doCheckScrollViewShouldFullHeight(totalData) {
      if (this.autoFullHeight && this.usePageScroll && this.isTotalChangeFromAddData) {
        // #ifndef APP-NVUE
        this.$nextTick(() => {
          this._checkScrollViewShouldFullHeight((scrollViewNode, pagingContainerNode) => {
            this._preCheckShowNoMoreInside(totalData, scrollViewNode, pagingContainerNode);
          });
        });
        // #endif
        // #ifdef APP-NVUE
        this._preCheckShowNoMoreInside(totalData);
        // #endif
      } else {
        this._preCheckShowNoMoreInside(totalData);
      }
    },
    //检测z-paging是否要全屏覆盖(当使用页面滚动并且不满全屏时，默认z-paging需要铺满全屏，避免数据过少时内部的empty-view无法正确展示)
    async _checkScrollViewShouldFullHeight(callback) {
      try {
        const scrollViewNode = await this._getNodeClientRect('.zp-scroll-view');
        const pagingContainerNode = await this._getNodeClientRect('.zp-paging-container-content');
        if (!scrollViewNode || !pagingContainerNode) return;
        const scrollViewHeight = pagingContainerNode[0].height;
        const scrollViewTop = scrollViewNode[0].top;
        if (this.isAddedData && scrollViewHeight + scrollViewTop <= this.windowHeight) {
          this._setAutoHeight(true, scrollViewNode);
          callback(scrollViewNode, pagingContainerNode);
        } else {
          this._setAutoHeight(false);
          callback(null, null);
        }
      } catch (e) {
        callback(null, null);
      }
    },
    //设置z-paging高度
    async _setAutoHeight(shouldFullHeight = true, scrollViewNode = null) {
      let heightKey = 'min-height';
      // #ifndef APP-NVUE
      heightKey = 'min-height';
      // #endif
      try {
        if (shouldFullHeight) {
          let finalScrollViewNode = scrollViewNode ? scrollViewNode : await this._getNodeClientRect('.zp-scroll-view');
          let finalScrollBottomNode = await this._getNodeClientRect('.zp-page-bottom');
          if (finalScrollViewNode) {
            const scrollViewTop = finalScrollViewNode[0].top;
            let scrollViewHeight = this.windowHeight - scrollViewTop;
            if (finalScrollBottomNode) {
              scrollViewHeight -= finalScrollBottomNode[0].height;
            }
            const additionHeight = u.convertTextToPx(this.autoHeightAddition);
            const finalHeight = scrollViewHeight + additionHeight - (this.insideMore ? 1 : 0) + 'px !important';
            this.$set(this.scrollViewStyle, heightKey, finalHeight);
            this.$set(this.scrollViewInStyle, heightKey, finalHeight);
          }
        } else {
          this.$delete(this.scrollViewStyle, heightKey);
          this.$delete(this.scrollViewInStyle, heightKey);
        }
      } catch (e) {}
    },
    //通过获取css设置的底部安全区域占位view高度设置bottom距离
    _getCssSafeAreaInsetBottom() {
      this._getNodeClientRect('.zp-safe-area-inset-bottom').then((res) => {
        if (res) {
          this.cssSafeAreaInsetBottom = res[0].height;
          if (this.safeAreaInsetBottom) {
            this.updatePageScrollBottomHeight();
          }
        }
      });
    },
    //触发更新是否超出页面状态
    _updateInsideOfPaging() {
      if (this.insideMore && this.insideOfPaging === true) {
        setTimeout(() => {
          this.doLoadMore();
        }, 200);
      }
    },
    //获取节点尺寸
    _getNodeClientRect(select, inThis = true, scrollOffset = false) {
      // #ifdef APP-NVUE
      select = select.replace('.', '').replace('#', '');
      const ref = this.$refs[select];
      return new Promise((resolve, reject) => {
        if (ref) {
          weexDom.getComponentRect(ref, (option) => {
            resolve(option && option.result ? [option.size] : false);
          });
        } else {
          resolve(false);
        }
      });
      return;
      // #endif
      //#ifdef MP-ALIPAY
      inThis = false;
      //#endif
      let res = inThis ? uni.createSelectorQuery().in(this) : uni.createSelectorQuery();
      scrollOffset ? res.select(select).scrollOffset() : res.select(select).boundingClientRect();
      return new Promise((resolve, reject) => {
        res.exec((data) => {
          resolve(data && data != '' && data != undefined && data.length ? data : false);
        });
      });
    },
    //清除timeout
    _cleanTimeout(timeout) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      return timeout;
    },
    //添加全局emit监听
    _onEmit() {
      uni.$on(c.errorUpdateKey, () => {
        if (this.loading) {
          this.complete(false);
        }
      });
      uni.$on(c.completeUpdateKey, (data) => {
        setTimeout(() => {
          if (this.loading) {
            if (!this.disabledCompleteEmit) {
              const type = data.type || 'normal';
              const list = data.list || data;
              const rule = data.rule;
              this.fromCompleteEmit = true;
              switch (type) {
                case 'normal':
                  this.complete(list);
                  break;
                case 'total':
                  this.completeByTotal(list, rule);
                  break;
                case 'nomore':
                  this.completeByNoMore(list, rule);
                  break;
                case 'key':
                  this.completeByKey(list, rule);
                  break;
                default:
                  break;
              }
            } else {
              this.disabledCompleteEmit = false;
            }
          }
        }, 1);
      });
    },
    //销毁全局emit和listener监听
    _offEmit() {
      uni.$off(c.errorUpdateKey);
      uni.$off(c.completeUpdateKey);
    },
  },
};
